package com.golxen.framework.redis;

import com.alibaba.fastjson.JSON;
import com.golxen.utils.LogUtil;
import com.golxen.utils.YmlUtil;
import com.mysql.cj.util.StringUtils;
import org.slf4j.Logger;
import redis.clients.jedis.*;

import java.util.*;

@SuppressWarnings("deprecation")
public class JedisUtils {
	private static Logger LOG = LogUtil.COMMON;

	private JedisPoolUtils pool;


	private static JedisUtils instance;


	public static JedisUtils getInstance() {

		if (instance == null) {
			instance = new JedisUtils();
		}
		return instance;
	}

	private JedisUtils() {
		init();
	}

	private void init() {
		JedisPoolConfig config = initConfig();
		String host = YmlUtil.getString("redis.host");
		int port = YmlUtil.getInt("redis.port");
		String requirepass = YmlUtil.getString("redis.password");
		int database=YmlUtil.getInt("redis.database");

		pool = new JedisPoolUtils(config, host, port, Protocol.DEFAULT_TIMEOUT,
				StringUtils.isNullOrEmpty(requirepass) ? null : requirepass,database);

	}

	public void reInit() {
		if (pool != null) {
			pool.destroy();

			init();
		}
	}

	/**
	 * 判断redis是否需要重连操作
	 *
	 * @return
	 */
	public boolean isNeedReconnect() {
		if (pool == null || pool.isClosed())
			return true;
		try {
			// 连接数量
			int activeNum = pool.getNumActive();
			if (activeNum < 0)
				return true;
			if (activeNum >= 50)
				return false;
		} catch (Exception e) {
			return true;
		}
		if (pool.getPoolFailCount() > 20)
			return true;

		return false;
	}

	/**
	 * 判断redis是否重连成功
	 *
	 * @return
	 */
	public boolean isSuccReconnect() {
		if (pool == null || pool.isClosed())
			return false;
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
		} catch (Exception e) {
			return false;
		} finally {
			close(jedis);
		}

		if (pool.getPoolFailCount() > 0)
			return false;

		return true;
	}

	private JedisPoolConfig initConfig() {
		JedisPoolConfig config = new JedisPoolConfig();
		config.setMaxTotal(YmlUtil.getInt("redis.pool.maxTotal"));
		config.setMaxIdle(YmlUtil.getInt("redis.pool.maxIdle"));
		config.setMinIdle(YmlUtil.getInt("redis.pool.minIdle"));
		config.setMaxWaitMillis(YmlUtil.getInt("redis.pool.maxWaitTime"));

		return config;
	}

	public void setObject(String key, Object obj) {

		Jedis jedis = null;
		try {
			jedis = pool.getResource();

			jedis.set(key, JSON.toJSONString(obj));
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	public <T> T getObject(String key, Class<T> t) {

		Jedis jedis = null;
		try {
			jedis = pool.getResource();

			String v = jedis.get(key);
			return JSON.parseObject(v, t);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);

		} finally {
			close(jedis);
		}
		return null;
	}

	/**
	 * 根据key获取一个String
	 *
	 * @param key
	 * @return
	 */
	public String get(String key) {

		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			String val = jedis.get(key);

			return val;
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return null;
	}

	public String flushAll() {

		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.flushAll();
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return null;
	}

	/**
	 * 保存value到redis中
	 *
	 * @param key
	 * @param value
	 */
	public void setString(String key, String value) {

		if (value == null) {
			return;
		}
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.set(key, value);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	public void setObject(String key, Object obj, int expirySeconds) {

		if (obj == null) {
			return;
		}
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.setex(key, expirySeconds, JSON.toJSONString(obj));
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	/**
	 * 保存一个map到redis中
	 *
	 * @param key
	 * @param map
	 */
	public void setObject(String key, Map<String, String> map) {

		if (map == null || map.isEmpty()) {
			return;
		}

		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.hmset(key, map);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	/**
	 * 获取一个map的元素数量
	 *
	 * @param key
	 * @return
	 */
	public long hlen(String key) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.hlen(key);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);

		} finally {
			close(jedis);
		}

		return 0;
	}

	/**
	 * 获取一个对象中的多个field的值
	 *
	 * @param key    指定一个对象
	 * @param fields 指定map中的多个key
	 * @return
	 */
	public List<String> getFieldsFromObject(String key, String... fields) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.hmget(key, fields);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);

		} finally {
			close(jedis);
		}

		return null;
	}

	/**
	 * 删除一个key
	 *
	 * @param key
	 */
	public void delete(String key) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.del(key);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	/**
	 * 判断一个key存不存在
	 *
	 * @param key
	 * @return
	 */
	public boolean exists(String key) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.exists(key);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}

		return false;
	}

	/**
	 * 将value保存到一个对象的字段中
	 *
	 * @param key
	 * @param field
	 * @param value
	 */
	public void setFieldToObject(String key, String field, String value) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.hset(key, field, value);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	public void setFieldsToObject(String key, Map<String, String> hash) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.hmset(key, hash);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	public void setFieldsToObject(String key, Map<String, String> hash, int seconds) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.hmset(key, hash);
			jedis.expire(key, seconds);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	public void expire(String key, int seconds) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.expire(key, seconds);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	/**
	 * 从一个对象中删除某个字段
	 *
	 * @param key
	 * @param field
	 */
	public void delFieldFromObject(String key, String field) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.hdel(key, field);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	/**
	 * 获取一个对象的某个字段的值
	 *
	 * @param key
	 * @param field
	 * @return
	 */
	public String getFieldFromObject(String key, String field) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.hget(key, field);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			return null;
		} finally {
			close(jedis);
		}
	}

	/**
	 * 获取整个Object
	 *
	 * @param key
	 * @return
	 */
	public Map<String, String> getMap(String key) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.hgetAll(key);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);

			return null;
		} finally {
			close(jedis);
		}
	}

	/**
	 * 加锁
	 * 
	 * @param key     锁名
	 * @param timeout 单位秒
	 */
	public boolean setnx(String key, int timeout) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			if (jedis.setnx(key, "TRUE") == 1) {
				jedis.expire(key, timeout);
				return true;
			} else {
				jedis.expire(key, timeout);
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return false;
	}

	public boolean lock(String lockKey, long timeout) {
		Jedis jedis = null;
		long nano = System.nanoTime();
		timeout *= 1000000000L;
		try {
			jedis = pool.getResource();
			while ((System.nanoTime() - nano) < timeout) {
				if (jedis.setnx(lockKey, "TRUE") == 1) {
					jedis.expire(lockKey, 8);
					return true;
				}
				// 短暂休眠，nano避免出现活锁
				Thread.sleep(3, (new Random()).nextInt(500));
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return false;
	}

	public void unlock(String lockKey) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.del(lockKey);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	/**
	 * 内存中的值+x
	 *
	 * @param key
	 * @param incrVal
	 * @return
	 */
	public long incrBy(String key, long incrVal) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.incrBy(key, incrVal);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return 0;
	}

	/**
	 * 向一个队列插入一条消息
	 *
	 * @param key
	 * @param val
	 */
	public void pushMsg(String key, String val) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.rpush(key, val);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	/**
	 * 从一个队列消费一条消息
	 *
	 * @param key
	 * @return
	 */
	public String popMsg(String key) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.lpop(key);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			return null;
		} finally {
			close(jedis);
		}
	}

	/**
	 * 获取list中指定中某些位置的元素
	 *
	 * @param key
	 * @param startIndex
	 * @param endIndex
	 * @return
	 */
	public List<String> getListElements(String key, int startIndex, int endIndex) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.lrange(key, startIndex, endIndex);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			return null;
		} finally {
			close(jedis);
		}
	}

	public String blockPopMsg(int timeOut, String... keys) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			List<String> ret = jedis.blpop(timeOut, keys);
			if (ret != null) {
				return ret.get(1);
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return null;
	}

	/**
	 * 获取一个
	 *
	 * @param key
	 * @return
	 */
	public List<String> getMapValues(String key) {

		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.hvals(key);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);

		} finally {
			close(jedis);
		}
		return null;
	}

	/**
	 * 获取一个
	 *
	 * @param key
	 * @return
	 */
	public void sadd(String key, String member) {

		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.sadd(key, member);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);

		} finally {
			close(jedis);
		}

	}

	/**
	 * 获取一个
	 *
	 * @param key
	 * @return
	 */
	public void srem(String key, String member) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.srem(key, member);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
	}

	/**
	 * 获取一个
	 *
	 * @param key
	 * @return
	 */
	public boolean sIsMember(String key, String member) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.sismember(key, member);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return false;
	}

	/**
	 * 获取一个
	 *
	 * @param key
	 * @return
	 */
	public Set<String> smembers(String key) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.smembers(key);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return null;
	}

	/**
	 * 求一个集合的元素个数
	 *
	 * @param key
	 * @return
	 */
	public long llen(String key) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.llen(key);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return 0;

	}

	public void zadd(String key, double score, String member) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			jedis.zadd(key, score, member);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);

		} finally {
			close(jedis);
		}
	}

	public Set<String> zmember(String key, double start, double end) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.zrangeByScore(key, start, end);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return null;
	}

	public Set<Tuple> zRangeWithScores(String key, long start, long end) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.zrangeWithScores(key, start, end);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return null;
	}

	public Double zscore(String key, String member) {

		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.zscore(key, member);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}

		return null;

	}

	public void close(Jedis jedis) {
		if (jedis != null) {
			try {
				jedis.close();
			} catch (Exception e) {
				LOG.error(e.getMessage(), e);
			}
		}
	}

	public boolean addMapValueByKey(String redisKey, String mapKey, Object value) {
		Map<String, String> mapStr = JedisUtils.getInstance().getMap(redisKey);
		if (mapStr == null)
			mapStr = new HashMap<String, String>();
		mapStr.put(mapKey, JSON.toJSONString(value));
		JedisUtils.getInstance().setObject(redisKey, mapStr);

		return true;
	}

	public boolean removeMapValueByKey(String redisKey, String mapKey) {
		Jedis jedis = null;
		try {
			jedis = JedisUtils.getInstance().getJRedis();
			if (jedis != null)
				jedis.clientSetname("RemoveMapData");
			jedis.hdel(redisKey, mapKey);
		} catch (Exception e) {
			LOG.error("Remove Map Value Error!", e);
		} finally {
			JedisUtils.getInstance().close(jedis);
		}

		return true;
	}


	public Jedis getJRedis() {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis;
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * 获取所有匹配的字符串的返回值
	 *
	 * @param pattern
	 * @return
	 */
	public Set<String> keys(String pattern) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.keys(pattern + "*");
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return null;
	}

	/**
	 * 获取所有匹配的字符串的Key对应的值
	 *
	 * @param pattern
	 * @return
	 */
	public List<String> getValues(String pattern) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			Set<String> keys = jedis.keys(pattern + "*");
			if (keys == null || keys.isEmpty())
				return null;
			String[] keyArr = new String[keys.size()];
			keyArr = keys.toArray(keyArr);
			return jedis.mget(keyArr);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			close(jedis);
		}
		return null;
	}

}
